<template>
  <el-form :model="config" label-width="120px" class="soft-form scrollable-form" @input="onInput" @blur="onBlur">
    <el-card header="弹幕违禁词惩罚" class="soft-card">
      <div style="display: flex; align-items: center; margin-bottom: 8px;">
        <el-switch v-model="config.punish_enable" style="margin-right: 8px;" @change="onSave" />
        <span style="font-size: 15px; color: #b48ad6; user-select: none;">启用违禁词禁言/拉黑（全局开关）</span>
      </div>
  <!-- 违禁词功能说明已移除，避免与气泡重复 -->
    </el-card>
    <el-card header="花花直播助手" class="soft-card">
      <div style="display: flex; align-items: center; margin-bottom: 8px;">
        <el-switch v-model="config.huahua.danmu_proxy_enable" style="margin-right: 8px;" @change="onSave" />
        <span style="font-size: 15px; color: #b48ad6; user-select: none;" @click.stop="showExplain('huahua.danmu_proxy_enable', $event)">花花弹幕接管（通过花花助手API发送弹幕）</span>
      </div>
  <div style="display: flex; align-items: center; margin-bottom: 8px;">
        <el-switch v-model="config.huahua.danmu_receive_enable" style="margin-right: 8px;" @change="onSave" />
        <span style="font-size: 15px; color: #b48ad6; user-select: none;" @click.stop="showExplain('huahua.danmu_receive_enable', $event)">花花弹幕接收（通过WebSocket接收弹幕）</span>
      </div>
  <!-- 花花直播助手功能说明已移除，避免与气泡重复 -->
    </el-card>
    <el-card header="AI参数" class="soft-card">
      <el-input v-model="config.ai.system_prompt" placeholder="系统提示词" class="soft-input"
        @click.stop="showExplain('ai.system_prompt', $event)" />
      <el-input v-model="config.ai.auto_reply" placeholder="无内容时自动回复" class="soft-input"
        @click.stop="showExplain('ai.auto_reply', $event)" />
      <el-input v-model="config.ai.keywords[0]" placeholder="唤醒词" class="soft-input"
        @click.stop="showExplain('ai.keywords', $event)" />
      <el-input v-if="config.punish_reply" v-model="config.punish_reply.mute" placeholder="被禁言时的自定义回复" class="soft-input" style="margin-top: 8px;"
        @click.stop="showExplain('punish_reply.mute', $event)" />
      <el-input v-if="config.punish_reply" v-model="config.punish_reply.kick" placeholder="被踢出/拉黑时的自定义回复" class="soft-input" style="margin-top: 8px;"
        @click.stop="showExplain('punish_reply.kick', $event)" />
      <el-input v-if="config.punish_reply" v-model="config.punish_reply.limit" placeholder="触发调用频率限制时的自定义回复" class="soft-input" style="margin-top: 8px;"
        @click.stop="showExplain('punish_reply.limit', $event)" />
      <el-select v-model="config.ai.provider" placeholder="选择AI服务商" class="soft-input" style="margin-bottom: 14px; margin-top: 8px;">
        <el-option label="OpenAI" value="openai" />
        <el-option label="Deepseek" value="deepseek" />
        <el-option label="小爱AI" value="xiaoi" />
      </el-select>
      <div v-if="config.ai.provider === 'openai'">
        <el-input v-model="config.openai.api_key" placeholder="OpenAI API Key" class="soft-input" show-password @click.stop="showExplain('openai.api_key', $event)" />
        <el-input v-model="config.openai.model" placeholder="OpenAI模型名" class="soft-input" @click.stop="showExplain('openai.model', $event)" />
        <el-input v-model="config.openai.max_response_length" placeholder="最大回复长度" class="soft-input" @click.stop="showExplain('openai.max_response_length', $event)" />
        <el-input v-model="config.openai.temperature" placeholder="采样温度" class="soft-input" @click.stop="showExplain('openai.temperature', $event)" />
      </div>
      <div v-if="config.ai.provider === 'deepseek'">
        <el-input v-model="config.deepseek.api_key" placeholder="Deepseek API Key" class="soft-input" show-password @click.stop="showExplain('deepseek.api_key', $event)" />
        <el-input v-model="config.deepseek.model" placeholder="Deepseek模型名" class="soft-input" @click.stop="showExplain('deepseek.model', $event)" />
        <el-input v-model="config.deepseek.max_tokens" placeholder="最大tokens" class="soft-input" @click.stop="showExplain('deepseek.max_tokens', $event)" />
        <el-input v-model="config.deepseek.temperature" placeholder="采样温度" class="soft-input" @click.stop="showExplain('deepseek.temperature', $event)" />
      </div>
      <div v-if="config.ai.provider === 'xiaoi'">
        <el-input v-model="config.xiaoi.api_key" placeholder="小爱AI API Key" class="soft-input" show-password @click.stop="showExplain('xiaoi.api_key', $event)" />
        <el-input v-model="config.xiaoi.api_url" placeholder="小爱AI接口地址" class="soft-input" @click.stop="showExplain('xiaoi.api_url', $event)" />
        <el-input v-model="config.xiaoi.model" placeholder="小爱AI模型名" class="soft-input" @click.stop="showExplain('xiaoi.model', $event)" />
        <el-input v-model="config.xiaoi.temperature" placeholder="采样温度" class="soft-input" @click.stop="showExplain('xiaoi.temperature', $event)" />
      </div>
    <el-card header="AI弹幕长度限制" class="soft-card" style="margin-bottom: 18px;" v-if="config.ai_danmu_limit">
      <el-input v-model="config.ai_danmu_limit.over_limit_tip" placeholder="超长回复自定义提示" style="margin-top: 8px;" />
      <div style="color:#b48ad6; font-size: 0.98rem; margin-top: 2px;">超出最大分段数时，自动发送此提示</div>
      <div style="display: flex; align-items: center; margin-bottom: 8px;">
  <el-switch v-model="config.ai_danmu_limit.enable" style="margin-right: 8px;" @change="onSave" />
        <span style="font-size: 15px; color: #b48ad6; user-select: none;">开启AI弹幕长度限制</span>
      </div>
      <el-row :gutter="8" style="margin-top: 6px;">
        <el-col :span="12">
          <el-input-number v-model="config.ai_danmu_limit.max_length" :min="10" :max="60" :step="1" label="最大汉字数" style="width: 120px;" />
          <span style="font-size:12px; margin-left: 6px;">最大汉字数（建议20-30，60字符）</span>
        </el-col>
        <el-col :span="12">
          <el-input-number v-model="config.ai_danmu_limit.max_segments" :min="1" :max="5" :step="1" label="最大分段数" style="width: 120px; margin-left: 12px;" />
          <span style="font-size:12px; margin-left: 6px;">最多分几段，超出用提示</span>
        </el-col>
      </el-row>
      <div style="display: flex; align-items: center; margin-top: 8px;">
  <el-switch v-model="config.ai_danmu_limit.concise_mode" style="margin-right: 8px;" @change="onSave" />
        <span style="font-size: 15px; color: #b48ad6; user-select: none;">精简回复模式（开启后AI回复更短，分段功能失效）</span>
      </div>
      <el-row :gutter="8" style="margin-top: 8px;">
        <el-col :span="12">
          <el-input-number v-model="config.ai_danmu_limit.reply_delay" :min="0" :max="10" :step="1" label="AI弹幕回复延迟（秒）" style="width: 120px;" />
          <span style="font-size:12px; margin-left: 6px;">AI弹幕回复延迟（防止B站限流，建议2-5秒）</span>
        </el-col>
      </el-row>
      <div style="color:#b48ad6; font-size: 0.98rem; margin-top: 6px;">开启后，AI自动弹幕回复将自动截断，保证弹幕正常发送。精简模式下AI回复更短，分段功能失效。延迟可防止B站频率限制。</div>
    </el-card>
  <!-- 去除重复的AI参数输入框，避免UI重复 -->
    </el-card>
  <el-card header="B站弹幕配置" class="soft-card">
      <el-input v-model="config.bilibili.uid" placeholder="主播UID" class="soft-input"
        @click.stop="showExplain('bilibili.uid', $event)" />
      <el-input v-model="config.bilibili.room_id" placeholder="房间号" class="soft-input"
        @click.stop="showExplain('bilibili.room_id', $event)" />
      <el-input v-model="config.bilibili.max_queue_size" placeholder="最大队列" class="soft-input"
        @click.stop="showExplain('bilibili.max_queue_size', $event)" />
      <el-input v-model="config.bilibili.retry_interval" placeholder="重试间隔" class="soft-input"
        @click.stop="showExplain('bilibili.retry_interval', $event)" />
      <div style="display: flex; align-items: center; margin-bottom: 8px;">
        <el-switch v-model="config.bilibili.use_alt_account" style="margin-right: 8px;" @change="onSave" />
        <span style="font-size: 15px; color: #b48ad6; user-select: none;">用小号接管主号逻辑（弹幕/AI等）</span>
      </div>
      <div style="display: flex; align-items: center; margin-bottom: 8px;">
  <el-switch v-model="config.bilibili.auto_save_cookie" style="margin-right: 8px;" @change="onSave" />
        <span style="font-size: 15px; color: #b48ad6; user-select: none;" @click.stop="showExplain('bilibili.auto_save_cookie', $event)">扫码后自动保存cookie</span>
      </div>
      <div style="display: flex; align-items: center; margin-bottom: 8px;">
  <el-switch v-model="config.bilibili.auto_cookie_renew" style="margin-right: 8px;" @change="onSave" />
        <span style="font-size: 15px; color: #b48ad6; user-select: none;" @click.stop="showExplain('bilibili.auto_cookie_renew', $event)">自动续期cookie</span>
      </div>
      <div style="display: flex; align-items: center; margin-bottom: 8px;">
  <el-switch v-model="config.bilibili.cookie_expire_remind" style="margin-right: 8px;" @change="onSave" />
        <span style="font-size: 15px; color: #b48ad6; user-select: none;" @click.stop="showExplain('bilibili.cookie_expire_remind', $event)">cookie过期提醒</span>
      </div>
      <div v-if="config.bilibili.join_limit && config.bilibili.join_limit.captain && config.bilibili.join_limit.admin && config.bilibili.join_limit.normal" style="margin: 10px 0 8px 0;">
        <span style="font-weight:bold;">弹幕调用频率限制：</span>
        <el-row :gutter="8" style="margin-top: 6px;">
          <el-col :span="8">
            <div style="font-size:13px;">舰长</div>
            <el-input-number v-model="config.bilibili.join_limit.captain.interval_minutes" :min="1" :max="60" size="small" style="width:90px;" :step="1" />
            <span style="font-size:12px;">分钟</span>
            <el-input-number v-model="config.bilibili.join_limit.captain.max_calls" :min="1" :max="999" size="small" style="width:90px; margin-left:6px;" :step="1" />
            <span style="font-size:12px;">次</span>
          </el-col>
          <el-col :span="8">
            <div style="font-size:13px;">房管</div>
            <el-input-number v-model="config.bilibili.join_limit.admin.interval_minutes" :min="1" :max="60" size="small" style="width:90px;" :step="1" />
            <span style="font-size:12px;">分钟</span>
            <el-input-number v-model="config.bilibili.join_limit.admin.max_calls" :min="1" :max="999" size="small" style="width:90px; margin-left:6px;" :step="1" />
            <span style="font-size:12px;">次</span>
          </el-col>
          <el-col :span="8">
            <div style="font-size:13px;">路人</div>
            <el-input-number v-model="config.bilibili.join_limit.normal.interval_minutes" :min="1" :max="60" size="small" style="width:90px;" :step="1" />
            <span style="font-size:12px;">分钟</span>
            <el-input-number v-model="config.bilibili.join_limit.normal.max_calls" :min="1" :max="999" size="small" style="width:90px; margin-left:6px;" :step="1" />
            <span style="font-size:12px;">次</span>
          </el-col>
        </el-row>
      </div>
      <el-input v-model="config.bilibili.cookie.SESSDATA" placeholder="SESSDATA" class="soft-input" show-password
        @click.stop="showExplain('bilibili.cookie.SESSDATA', $event)" />
      <el-input v-model="config.bilibili.cookie.bili_jct" placeholder="bili_jct" class="soft-input" show-password
        @click.stop="showExplain('bilibili.cookie.bili_jct', $event)" />
      <el-input v-model="config.bilibili.cookie.DedeUserID" placeholder="DedeUserID" class="soft-input" show-password
        @click.stop="showExplain('bilibili.cookie.DedeUserID', $event)" />
    </el-card>
    <el-card header="TTS 配置" class="soft-card">
      <div class="soft-row">
        <div style="display: flex; align-items: center; margin-bottom: 8px;">
          <el-switch v-model="config.tts.enable" style="margin-right: 8px;" @change="onSave" />
          <span style="font-size: 15px; color: #b48ad6; user-select: none;" @click.stop="showExplain('tts.enable', $event)">启用TTS</span>
        </div>
        <div style="display: flex; align-items: center; margin-bottom: 8px;">
          <el-switch v-model="config.tts.send_danmu" style="margin-right: 8px;" @change="onSave" />
          <span style="font-size: 15px; color: #b48ad6; user-select: none;" @click.stop="showExplain('tts.send_danmu', $event)">自动弹幕</span>
        </div>
      </div>
      <el-input v-model="config.tts.engine" placeholder="TTS引擎" class="soft-input"
        @click.stop="showExplain('tts.engine', $event)" />
      <el-input v-model="config.tts.audio_cache_dir" placeholder="缓存路径" class="soft-input"
        @click.stop="showExplain('tts.audio_cache_dir', $event)" />
      <el-input-number v-model="config.tts.audio_cache_max_files" :min="1" label="最大缓存数" class="soft-input"
        @click.stop="showExplain('tts.audio_cache_max_files', $event)" />
      <div style="display: flex; align-items: center; margin-bottom: 8px;">
  <el-switch v-model="config.tts.enable_tts_params" style="margin-right: 8px;" @change="onSave" />
        <span style="font-size: 15px; color: #b48ad6; user-select: none;" @click.stop="showExplain('tts.enable_tts_params', $event)">自定义TTS参数</span>
      </div>
      <el-input v-model="config.tts.sovits_url" placeholder="SoVITS接口URL" class="soft-input"
        @click.stop="showExplain('tts.sovits_url', $event)" />
      <el-input v-model="config.tts.vits_url" placeholder="VITS接口URL" class="soft-input"
        @click.stop="showExplain('tts.vits_url', $event)" />
      <el-input v-model="config.tts.huahua_url" placeholder="花花接口URL" class="soft-input"
        @click.stop="showExplain('tts.huahua_url', $event)" />
      <el-input v-model="config.tts.custom_url" placeholder="自定义TTS接口URL" class="soft-input"
        @click.stop="showExplain('tts.custom_url', $event)" />
    </el-card>
    <el-card header="日志与系统" class="soft-card">
      <el-switch v-model="config.logging.file_logging" @change="onSave" active-text="文件日志"
        @click.stop="showExplain('logging.file_logging', $event)" />
      <el-input v-model="config.logging.level" placeholder="日志等级" class="soft-input"
        @click.stop="showExplain('logging.level', $event)" />
      <el-input v-model="config.logging.log_dir" placeholder="日志目录" class="soft-input"
        @click.stop="showExplain('logging.log_dir', $event)" />
      <el-input v-model="config.system.log_level" placeholder="系统日志等级" class="soft-input"
        @click.stop="showExplain('system.log_level', $event)" />
      <el-input v-model="config.system.system_cooldown" placeholder="系统冷却(s)" class="soft-input"
        @click.stop="showExplain('system.system_cooldown', $event)" />
      <el-input v-model="config.system.user_cooldown" placeholder="用户冷却(s)" class="soft-input"
        @click.stop="showExplain('system.user_cooldown', $event)" />
      <el-input v-model="config.system.worker_threads" placeholder="线程数" class="soft-input"
        @click.stop="showExplain('system.worker_threads', $event)" />
    </el-card>
    <div class="soft-btn-row">
      <el-button type="primary" @click="onSave" class="soft-btn">保存配置</el-button>
      <el-button type="warning" @click="onRestoreDefault" class="soft-btn" style="margin-left: 18px;">恢复默认配置</el-button>
    </div>
    <!-- 说明气泡 -->
    <div v-if="explainVisible" class="explain-bubble"
      :style="{ left: explainPos.x + 'px', top: explainPos.y + 'px' }">
      {{ explainText }}
    </div>
  </el-form>
</template>
<script setup>
import { ref, onMounted, onUnmounted, nextTick } from 'vue'
import { getConfig, saveConfig } from '../api'
import { ElMessage } from 'element-plus'
const defaultConfig = {
  huahua: { danmu_proxy_enable: false, danmu_receive_enable: false },
  ai: { system_prompt: '', auto_reply: '', provider: '', keywords: [''] },
  punish_reply: { mute: '', kick: '', limit: '' },
  bilibili: {
    room_id: '',
    max_queue_size: '100',
    retry_interval: '5',
    auto_save_cookie: false,
    auto_cookie_renew: false,
    cookie_expire_remind: false,
    cookie: { SESSDATA: '', bili_jct: '', DedeUserID: '' },
    join_limit: {
      captain: { interval_minutes: 1, max_calls: 3 },
      admin: { interval_minutes: 2, max_calls: 3 },
      normal: { interval_minutes: 3, max_calls: 2 }
    }
  },
  tts: {
    enable: false,
    send_danmu: false,
    engine: '',
    audio_cache_dir: '',
    audio_cache_max_files: 10,
    enable_tts_params: false,
    sovits_url: '',
    vits_url: '',
    huahua_url: '',
    custom_url: ''
  },
  deepseek: {
    api_key: '',
    base_url: '',
    max_tokens: '100',
    model: '',
    temperature: '1.0'
  },
  openai: {
    api_key: '',
    base_url: '',
    max_response_length: '100',
    model: '',
    temperature: '0.7'
  },
  xiaoi: {
    api_key: '',
    api_url: '',
    model: '',
    temperature: '0.7'
  },
  logging: {
    file_logging: false,
    level: 'INFO',
    log_dir: ''
  },
  system: {
    log_level: 'INFO',
    system_cooldown: '2',
    user_cooldown: '2',
    worker_threads: '2'
  },
  ai_danmu_limit: {
    enable: true,
    max_length: 30,
    max_segments: 2,
    over_limit_tip: '[AI回复过长，剩余内容未显示]',
    concise_mode: false,
    reply_delay: 3
  }
}
const config = ref(JSON.parse(JSON.stringify(defaultConfig)))
const onRestoreDefault = async () => {
  config.value = JSON.parse(JSON.stringify(defaultConfig))
  await saveConfig(config.value)
  ElMessage.success('已恢复默认配置~')
  await load()
}
let isEditing = false
let refreshTimer = null
const load = async () => {
  const res = await getConfig()
  const data = res.data || res
  // 调试输出，确保拿到的结构正确
  console.log('[设置中心] config接口返回', JSON.stringify(data))
  if (!data.ai) data.ai = { keywords: [''] }
  if (!data.ai.keywords) data.ai.keywords = ['']
  if (typeof data.ai.auto_reply !== 'string') data.ai.auto_reply = ''
  if (typeof data.ai.system_prompt !== 'string') data.ai.system_prompt = ''
  if (typeof data.ai.provider !== 'string') data.ai.provider = ''
  if (!data.bilibili) data.bilibili = { cookie: {} }
  if (!data.bilibili.cookie) data.bilibili.cookie = {}
  // join_limit结构兜底，只有存在时才处理
  if (data.bilibili.join_limit && typeof data.bilibili.join_limit === 'object') {
    if (!data.bilibili.join_limit.captain || typeof data.bilibili.join_limit.captain !== 'object') data.bilibili.join_limit.captain = { interval_minutes: 1, max_calls: 3 }
    if (!data.bilibili.join_limit.admin || typeof data.bilibili.join_limit.admin !== 'object') data.bilibili.join_limit.admin = { interval_minutes: 2, max_calls: 3 }
    if (!data.bilibili.join_limit.normal || typeof data.bilibili.join_limit.normal !== 'object') data.bilibili.join_limit.normal = { interval_minutes: 3, max_calls: 2 }
    ['captain','admin','normal'].forEach(role => {
      let item = data.bilibili.join_limit[role]
      if (!item || typeof item !== 'object') return
      if (typeof item.interval_minutes !== 'number') item.interval_minutes = Number(item.interval_minutes)||1
      if (typeof item.max_calls !== 'number') item.max_calls = Number(item.max_calls)||1
    })
  }
  if (typeof data.bilibili.max_queue_size !== 'string') data.bilibili.max_queue_size = String(data.bilibili.max_queue_size||'100')
  if (typeof data.bilibili.retry_interval !== 'string') data.bilibili.retry_interval = String(data.bilibili.retry_interval||'5')
  if (typeof data.bilibili.room_id !== 'string') data.bilibili.room_id = String(data.bilibili.room_id||'')
  if (typeof data.bilibili.auto_save_cookie !== 'boolean') data.bilibili.auto_save_cookie = !!data.bilibili.auto_save_cookie
  if (typeof data.bilibili.auto_cookie_renew !== 'boolean') data.bilibili.auto_cookie_renew = !!data.bilibili.auto_cookie_renew
  if (typeof data.bilibili.cookie_expire_remind !== 'boolean') data.bilibili.cookie_expire_remind = !!data.bilibili.cookie_expire_remind
  if (!data.huahua) data.huahua = { danmu_proxy_enable: false, danmu_receive_enable: false }
  if (typeof data.huahua.danmu_proxy_enable !== 'boolean') data.huahua.danmu_proxy_enable = false
  if (typeof data.huahua.danmu_receive_enable !== 'boolean') data.huahua.danmu_receive_enable = false
  if (!data.tts) data.tts = {}
  if (typeof data.tts.audio_cache_dir !== 'string') data.tts.audio_cache_dir = ''
  if (typeof data.tts.audio_cache_max_files !== 'number') data.tts.audio_cache_max_files = Number(data.tts.audio_cache_max_files)||10
  if (typeof data.tts.custom_url !== 'string') data.tts.custom_url = ''
  if (typeof data.tts.engine !== 'string') data.tts.engine = ''
  if (typeof data.tts.huahua_url !== 'string') data.tts.huahua_url = ''
  if (typeof data.tts.sovits_url !== 'string') data.tts.sovits_url = ''
  if (typeof data.tts.vits_url !== 'string') data.tts.vits_url = ''
  if (typeof data.tts.enable !== 'boolean') data.tts.enable = !!data.tts.enable
  if (typeof data.tts.enable_tts_params !== 'boolean') data.tts.enable_tts_params = !!data.tts.enable_tts_params
  if (typeof data.tts.send_danmu !== 'boolean') data.tts.send_danmu = !!data.tts.send_danmu
  if (!data.deepseek) data.deepseek = {}
  if (typeof data.deepseek.api_key !== 'string') data.deepseek.api_key = ''
  if (typeof data.deepseek.base_url !== 'string') data.deepseek.base_url = ''
  if (typeof data.deepseek.max_tokens !== 'string') data.deepseek.max_tokens = '100'
  if (typeof data.deepseek.model !== 'string') data.deepseek.model = ''
  if (typeof data.deepseek.temperature !== 'string') data.deepseek.temperature = '1.0'
  if (!data.openai) data.openai = {}
  if (typeof data.openai.api_key !== 'string') data.openai.api_key = ''
  if (typeof data.openai.base_url !== 'string') data.openai.base_url = ''
  if (typeof data.openai.max_response_length !== 'string') data.openai.max_response_length = '100'
  if (typeof data.openai.model !== 'string') data.openai.model = ''
  if (typeof data.openai.temperature !== 'string') data.openai.temperature = '0.7'
  if (!data.logging) data.logging = {}
  if (typeof data.logging.file_logging !== 'boolean') data.logging.file_logging = !!data.logging.file_logging
  if (typeof data.logging.level !== 'string') data.logging.level = 'INFO'
  if (typeof data.logging.log_dir !== 'string') data.logging.log_dir = ''
  if (!data.system) data.system = {}
  if (typeof data.system.log_level !== 'string') data.system.log_level = 'INFO'
  if (typeof data.system.system_cooldown !== 'string') data.system.system_cooldown = '2'
  if (typeof data.system.user_cooldown !== 'string') data.system.user_cooldown = '2'
  if (typeof data.system.worker_threads !== 'string') data.system.worker_threads = '2'
  if (!data.ai_danmu_limit) data.ai_danmu_limit = { enable: true, max_length: 30, max_segments: 2, over_limit_tip: '[AI回复过长，剩余内容未显示]', concise_mode: false, reply_delay: 3 }
  if (typeof data.ai_danmu_limit.enable !== 'boolean') data.ai_danmu_limit.enable = !!data.ai_danmu_limit.enable
  if (typeof data.ai_danmu_limit.max_length !== 'number') data.ai_danmu_limit.max_length = Number(data.ai_danmu_limit.max_length)||30
  if (typeof data.ai_danmu_limit.max_segments !== 'number') data.ai_danmu_limit.max_segments = Number(data.ai_danmu_limit.max_segments)||2
  if (typeof data.ai_danmu_limit.over_limit_tip !== 'string') data.ai_danmu_limit.over_limit_tip = '[AI回复过长，剩余内容未显示]'
  if (typeof data.ai_danmu_limit.concise_mode !== 'boolean') data.ai_danmu_limit.concise_mode = !!data.ai_danmu_limit.concise_mode
  if (typeof data.ai_danmu_limit.reply_delay !== 'number') data.ai_danmu_limit.reply_delay = Number(data.ai_danmu_limit.reply_delay)||3
  // 新增：punish_reply 字段兜底，防止 undefined
  if (!data.punish_reply) data.punish_reply = { mute: '', kick: '', limit: '' }
  if (typeof data.punish_reply.mute !== 'string') data.punish_reply.mute = ''
  if (typeof data.punish_reply.kick !== 'string') data.punish_reply.kick = ''
  if (typeof data.punish_reply.limit !== 'string') data.punish_reply.limit = ''
  if (!isEditing) {
    // 彻底兜底，防止config.value未被正确赋值
    config.value = JSON.parse(JSON.stringify(data))
    // 再次调试输出
    console.log('[设置中心] config.value赋值后', JSON.stringify(config.value))
  }
}
onMounted(() => {
  load()
  refreshTimer = setInterval(load, 5000)
  document.addEventListener('click', hideExplain)
})
onUnmounted(() => {
  if (refreshTimer) clearInterval(refreshTimer)
  document.removeEventListener('click', hideExplain)
})
const onInput = () => { isEditing = true }
const onBlur = () => { isEditing = false }
// 深度合并函数
function deepMerge(oldObj, newObj) {
  if (typeof oldObj !== 'object' || typeof newObj !== 'object' || !oldObj || !newObj) return newObj;
  const merged = Array.isArray(oldObj) ? [...oldObj] : { ...oldObj };
  for (const key in newObj) {
    if (newObj[key] && typeof newObj[key] === 'object' && !Array.isArray(newObj[key]) && oldObj[key]) {
      merged[key] = deepMerge(oldObj[key], newObj[key]);
    } else {
      merged[key] = newObj[key];
    }
  }
  return merged;
}
const onSave = async () => {
  // 先拉取最新配置
  const res = await getConfig();
  const latest = res.data || res;
  // 合并用户更改
  const merged = deepMerge(latest, config.value);
  await saveConfig(merged);
  ElMessage.success('配置已保存~ฅ^•ﻌ•^ฅ');
  await load();
}
// 说明表和气泡逻辑
const explains = {
  'huahua.danmu_proxy_enable': '开启后，弹幕将通过花花直播助手接口发送',
  'huahua.danmu_receive_enable': '开启后，将通过花花直播助手WebSocket实时接收弹幕',
  'bilibili.uid': '主播UID，部分接口和观众身份识别需要填写（通常与DedeUserID一致，扫码登录后自动写入）',
  'punish_reply.mute': '用户被禁言时自动发送的弹幕提示，可自定义内容',
  'punish_reply.kick': '用户被踢出/拉黑时自动发送的弹幕提示，可自定义内容',
  'ai_danmu_limit.reply_delay': 'AI弹幕回复延迟（秒），防止触发B站频率限制，建议2-5秒',
  'ai_danmu_limit.concise_mode': '精简回复开关，开启后AI需精简回复并严格控制在B站弹幕长度内，分段功能失效',
  'ai_danmu_limit.over_limit_tip': '超出最大分段数时自动发送的提示内容',
  'ai_danmu_limit.max_segments': 'AI弹幕最多分几段，超出部分用提示代替',
  'ai_danmu_limit.enable': '开启后，AI自动弹幕回复将自动截断，保证弹幕正常发送',
  'ai_danmu_limit.max_length': 'AI弹幕最大汉字数，建议20-30，或60字符以内',
  'ai.system_prompt': 'AI系统提示词，影响AI风格',
  'punish_reply.limit': '用户触发调用频率限制时自动发送的弹幕提示，可自定义内容',
  'ai.auto_reply': 'AI默认自动回复内容',
  'ai.provider': 'AI服务提供商（如 deepseek、openai）',
  'ai.keywords': '唤醒AI的关键词（如“六月”）',
  'bilibili.room_id': 'B站直播间房间号',
  'bilibili.max_queue_size': '弹幕处理队列最大长度',
  'bilibili.retry_interval': '弹幕重连间隔（秒）',
  'bilibili.auto_save_cookie': '扫码后自动保存cookie',
  'bilibili.auto_cookie_renew': 'cookie自动续期（到期前自动刷新cookie，需扫码登录）',
  'bilibili.cookie_expire_remind': 'cookie过期提醒（到期前弹窗提醒需重新扫码）',
  'bilibili.cookie.SESSDATA': 'B站 SESSDATA',
  'bilibili.cookie.bili_jct': 'B站 bili_jct',
  'bilibili.cookie.DedeUserID': 'B站 DedeUserID',
  'tts.enable': '是否启用TTS语音合成',
  'tts.send_danmu': 'AI回复是否自动发送弹幕',
  'tts.engine': 'TTS引擎类型（如gpt-sovits、huahua等）',
  'tts.audio_cache_dir': 'TTS音频缓存目录',
  'tts.audio_cache_max_files': 'TTS缓存音频最大数量',
  'tts.enable_tts_params': '自定义TTS参数开关',
  'tts.sovits_url': 'SoVITS TTS接口URL',
  'tts.vits_url': 'VITS TTS接口URL',
  'tts.huahua_url': '花花TTS接口URL',
  'tts.custom_url': '自定义TTS接口URL',
  'deepseek.api_key': 'Deepseek API密钥',
  'deepseek.max_tokens': '最大tokens',
  'deepseek.model': 'Deepseek模型名',
  'deepseek.temperature': '采样温度',
  'openai.api_key': 'OpenAI API密钥',
  'openai.max_response_length': '最大回复长度',
  'openai.model': 'OpenAI模型名',
  'openai.temperature': '采样温度',
  'logging.file_logging': '是否写入文件日志',
  'logging.level': '日志等级',
  'logging.log_dir': '日志目录',
  'system.log_level': '系统日志等级',
  'system.system_cooldown': '全局冷却时间（秒）',
  'system.user_cooldown': '用户冷却时间（秒）',
  'system.worker_threads': '线程数'
}
const explainText = ref('')
const explainVisible = ref(false)
const explainPos = ref({ x: 0, y: 0 })
function showExplain(key, event) {
  explainText.value = explains[key] || ''
  if (!explainText.value) return
  explainVisible.value = true
  nextTick(() => {
    const rect = event.target.getBoundingClientRect()
    explainPos.value = { x: rect.right + 8, y: rect.top - 4 }
  })
}
function hideExplain() {
  explainVisible.value = false
}
</script>
<style scoped>
.soft-form {
  margin: 24px 0 0 0;
}
.scrollable-form {
  /* 移除所有高度和滚动限制，交由外层容器控制 */
  box-sizing: border-box;
}
.soft-card {
  border-radius: 28px;
  box-shadow: 0 2px 12px #ffe0f7;
  margin-bottom: 22px;
  background: #fff6fa;
  border: none;
  padding-bottom: 8px;
  transition: box-shadow 0.2s;
}
.soft-card:not(:last-child) {
  border-bottom: 2px dashed #ffd6e6;
}
.soft-card:hover {
  box-shadow: 0 6px 24px #ffd6e6;
}
.soft-row {
  display: flex;
  gap: 18px;
  margin-bottom: 12px;
}
.soft-input {
  margin-bottom: 14px;
  border-radius: 16px;
  box-shadow: 0 1px 8px #ffe0f7;
  background: #fff;
  border: 1.5px solid #ffe0f7;
  font-size: 1.08rem;
  padding: 6px 12px;
  transition: border 0.15s, box-shadow 0.15s;
}
.soft-input:focus-within {
  border: 1.5px solid #ff90b3;
  box-shadow: 0 2px 12px #ffd6e6;
}
.soft-btn-row {
  display: flex;
  justify-content: center;
  margin-top: 12px;
}
.soft-btn {
  background: linear-gradient(90deg, #ffb6e6 0%, #b2f7ef 100%);
  border: none;
  color: #fff;
  font-weight: bold;
  border-radius: 20px;
  box-shadow: 0 2px 8px #ffd6e6;
  padding: 10px 38px;
  font-size: 1.13rem;
  letter-spacing: 1px;
  transition: filter 0.15s, box-shadow 0.15s;
}
.soft-btn:hover {
  filter: brightness(1.09);
  box-shadow: 0 4px 16px #ffd6e6;
}
.explain-bubble {
  position: fixed;
  z-index: 9999;
  max-width: 260px;
  background: #fffbe8;
  color: #a78bfa;
  border: 1px solid #f9a8d4;
  border-radius: 8px;
  padding: 10px 16px;
  font-size: 14px;
  box-shadow: 0 2px 8px #f9a8d4aa;
  pointer-events: none;
}
</style>